import os from "os";
import fs from 'fs'
import {fileURLToPath} from 'url';
import {dirname} from 'path';
// const {
//     createAll,
//     protectToString,
//     newFunc,
//     newObj,
//     callback,
//     Window,
//     window,
//     EventTarget,
//     WindowProperties,
//     getFuncToString,
//     Function,
//     myFunction
// } = require('./node_plugin/build/Release/env')
// import fs from "fs";

// import process from "process";

// const syncRequest = require('./dom/httpUtil')
import {CookieJar} from "tough-cookie";

import * as cheerio from "cheerio";

import Canvas from "canvas";

import crypto from "crypto";

import cyb, {createAll, newFunc} from "cyb";

let _newFunc = newFunc;
let convertNewFunc = function (name,argsLen, func){
    let res = _newFunc(name, argsLen, func)
    delete res.prototype.constructor
    // if (name.includes('Element'))res.call = Function.prototype.call
    res = new Proxy(res, {
        get(target, p, receiver) {
            let res = Reflect.get(target,p,receiver);
            if (p === 'call' && res === undefined){
                // console.log(`${name}.call.get is undefined`)
                // func.call = Function.prototype.call
                res = func
            }
            return res
        }
    })
    return res

}

// import {MongoClient} from "mongodb";
//
// import {performance} from "perf_hooks";

// const syncRequest = require('./httpUtil')
// const syncRequest
// 导入自定义的node插件
// import wn from "worker_threads";
// {parentPort, postMessage, Worker}
// 遍历获取所有环境文件
// console.log(envCode)
// var ENVS = JSON.parse(fs.readFileSync('./env.json').toString())
// let envs = [];
// Object.keys(ENVS).forEach(e => {
//     envs.push(ENVS[e])
// })
import vm from "vm";


// const ffi = require('ffi-napi');
// 判断设备是否为linux

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);


let path;
// console.log(process.arch)
if (os.platform() === 'linux') {
    path = './tls-client-linux-ubuntu-amd64-v1.7.2.so';
} else if (process.arch === 'arm64') {
    path = 'deps/tls-client-arm64.dylib'
} else {
    path = 'deps/tls-client-x86.dylib'

}


// const cronetjs = require('cronet')
//
// const {
//     CronetBuffer,
//     CronetHttpHeader,
//     CronetEngineParams,
//     CronetEngine,
//     CronetExecutor,
//     CronetUploadDataProvider,
//     CronetUrlRequestCallback,
//     CronetUrlRequestParams,
//     CronetUrlRequest,
// } = cronetjs

// dynamic load library
// CronetEngine.loadLibrary('/Users/wang/PycharmProjects/mine-vm/lib/libcronet.124.0.6344.0.dylib')
// CronetEngine.loadLibrary('/Users/wang/Documents/mine-vm/lib/libcronet.122.0.6261.112.dylib')

// dynamic load library
const plt = process.platform;
let uri;
if (plt !== 'darwin') {
    uri = "mongodb://lcc-spider:JIUsfhiad&^@13.52.65.111:27017/?authSource=admin&directConnection=true";
} else {
    uri = 'mongodb://root:123456@192.168.50.110:27017/'
}
// const client = new MongoClient(uri);
// const database = client.db("env");
// const movies = database.collection("env_shape");

// Query for a movie that has the title 'The Room'


function WriteToFile(filepath, text) {
    fs.writeFileSync(filepath, text)
}

// load the tls-client shared package for your OS you are currently running your nodejs script (i'm running on mac)
// const tlsClientLibrary = ffi.Library(path, {
//     'request': ['string', ['string']],
//     'getCookiesFromSession': ['string', ['string']],
//     'addCookiesToSession': ['string', ['string']],
//     'freeMemory': ["void", ['string']],
//     'destroyAll': ['string', []],
//     'destroySession': ['string', ['string']]
// });


function syncRequest(method, url, headers, proxy, data) {

    const requestPayload = {
        "tlsClientIdentifier": "chrome_117",
        "followRedirects": true,
        "insecureSkipVerify": false,
        "withoutCookieJar": false,
        "withDefaultCookieJar": false,
        "isByteRequest": false,
        "catchPanics": true,
        "withDebug": false,
        "forceHttp1": false,
        "withRandomTLSExtensionOrder": false,
        "timeoutSeconds": 5,
        "timeoutMilliseconds": 0,
        "sessionId": "my-session-id",
        "proxyUrl": "",
        "isRotatingProxy": false,
        "certificatePinningHosts": {},
        "headers": headers,
        "headerOrder": [
            "accept",
            "user-agent",
            "accept-encoding",
            "accept-language"
        ],
        "requestUrl": url,
        "requestMethod": method,
        "requestBody": data,
        "requestCookies": []
    }

// call the library with the requestPayload as string
    debugger;
    const response = tlsClientLibrary.request(JSON.stringify(requestPayload));


// convert response string to json
    const responseObject = JSON.parse(response)

    // console.log(responseObject)
    tlsClientLibrary.freeMemory(responseObject.id)


    const destroySessionPayload = {
        sessionId: 'my-session-id',
    }

    const destroySessionResponse = tlsClientLibrary.destroySession(JSON.stringify(destroySessionPayload))

    const destroySessionResponseParsed = JSON.parse(destroySessionResponse)
    tlsClientLibrary.destroyAll()
    // console.log(destroySessionResponseParsed)
    return responseObject
}

// 遍历文件夹
function walkSync(dir, filelist = []) {
    fs.readdirSync(dir).forEach(file => {
        const path = `${dir}/${file}`;
        const stat = fs.statSync(path);
        if (stat.isDirectory()) {
            filelist = walkSync(path, filelist);
        } else {
            filelist.push(path);
        }
    });
    return filelist;
}
let envCodeCache = null;
function getEnvCode() {
    if (envCodeCache !== null){
        return envCodeCache
    }
    let code = '';
    let base_path = __dirname + '/'
    let base_code = [
        'dom/env.js',
        'dom/vm_base.js',
        'dom/timer.js',


        // mouse
        'dom/EventTarget/EventTarget.js',

        'dom/EVENT/EVENT.js',

        'dom/EVENTS/UIEvent.js',
        'dom/EVENTS/TouchEvent.js',
        'dom/EVENTS/MouseEvent.js',
        'dom/EVENTS/PointerEvent.js',

        'dom/EventTarget/Node.js',
        'dom/EventTarget/NetworkInformation.js',
        'dom/EventTarget/StorageManager.js',

        'dom/window/VisualViewport.js',
        'dom/window/Window.js',
        'dom/window/File.js',


        'dom/CSS/CSSStyleDeclaration.js',
        'dom/CSS/CSSPositionTryDescriptors.js',
        'dom/DOM/Attr.js',

        'dom/EVENT/BaseAudioContext.js',
        'dom/EVENT/OfflineAudioContext.js',
        'dom/EVENT/Bluetooth.js',
        'dom/EVENT/BlobEvent.js',

        'dom/Performance/PerformanceEntry.js',

        'dom/Performance/Performance.js',
        'dom/Performance/PerformanceNavigation.js',
        'dom/Performance/PerformancePaintTiming.js',
        'dom/Performance/PerformanceTiming.js',
        'dom/Performance/PerformanceObserver.js',


        'dom/xhr/XMLHttpRequestEventTarget.js',
        'dom/xhr/XMLHttpRequest.js',
        'dom/xhr/fetch.js',

        'dom/EVENT/CustomEvent.js',


        'dom/BOM/Navigator.js',
        'dom/BOM/Plugin/MimeType.js',
        'dom/BOM/Plugin/MimeTypeArray.js',
        'dom/BOM/Plugin/Plugin.js',
        'dom/BOM/Plugin/PluginArray.js',
        'dom/BOM/Location.js',
        'dom/BOM/History.js',
        'dom/BOM/Screen.js',
        'dom/BOM/Storage.js',
        'dom/BOM/chrome.js',
        'dom/BOM/TextMetrics.js',
        'dom/BOM/MediaQueryList.js',
        'dom/BOM/BarProp.js',
        'dom/BOM/External.js',

        'dom/BOM/RTC/rtc.js',
        'dom/BOM/RTC/RTCDataChannel.js',
        'dom/BOM/RTC/RTCIceCandidate.js',
        'dom/BOM/RTC/RTCPeerConnection.js',
        'dom/BOM/RTC/RTCPeerConnectionIceEvent.js',
        'dom/BOM/RTC/RTCSessionDescription.js',
        'dom/BOM/Navigation.js',
        'dom/BOM/CookieStore.js',
        'dom/BOM/LaunchQueue.js',
        'dom/BOM/SharedStorage.js',
        'dom/BOM/DocumentPictureInPicture.js',
        'dom/BOM/GPU/GPU.js',
        'dom/BOM/GPU/GPUBufferUsage.js',
        'dom/BOM/GPU/GPUColorWrite.js',




        'dom/DOM/Document.js',
        'dom/DOM/elements/ElementsAttrs.js',
        'dom/DOM/Element.js',
        'dom/DOM/HTMLElement.js',
        'dom/DOM/DOMTokenList.js',
        'dom/DOM/elements/HTMLAllCollection.js',

        'dom/BOM/SVG/svg.js',
        'dom/BOM/USB/usb.js',
        'dom/BOM/MEDIA/media.js',
        'dom/BOM/Text/Text.js',
        'dom/BOM/CSS/css.js',
        'dom/BOM/XR/xr.js',
        'dom/BOM/BLUETOOTH/bluetooth.js',

        'dom/DOM/WebGL.js',
        'dom/DOM/WebGLRenderingContext.js',
        'dom/DOM/WebGL2RenderingContext.js',
        'dom/DOM/CanvasRenderingContext2D.js',
        'dom/DOM/OffscreenCanvas.js',
        'dom/DOM/elements/HTMLDocument.js',
        'dom/DOM/elements/HTMLParagraphElement.js',
        'dom/DOM/elements/HTMLCanvasElement.js',
        'dom/DOM/elements/HTMLBodyElement.js',
        'dom/DOM/elements/HTMLHeadElement.js',
        'dom/DOM/elements/HTMLHtmlElement.js',
        'dom/DOM/elements/HTMLMediaElement.js',
        'dom/DOM/elements/HTMLSpanElement.js',
        'dom/DOM/elements/HTMLFormElement.js',
        'dom/DOM/elements/HTMLAnchorElement.js',
        'dom/DOM/elements/HTMLLIElement.js',
        'dom/DOM/elements/HTMLImageElement.js',
        'dom/DOM/elements/HTMLScriptElement.js',
        'dom/DOM/elements/HTMLStyleElement.js',
        'dom/DOM/elements/HTMLTitleElement.js',
        'dom/DOM/elements/HTMLIFrameElement.js',
        'dom/DOM/elements/HTMLLinkElement.js',
        'dom/DOM/elements/HTMLUListElement.js',
        'dom/DOM/elements/HTMLDivElement.js',
        'dom/DOM/elements/HTMLAudioElement.js',
        'dom/DOM/elements/HTMLMetaElement.js',
        'dom/DOM/elements/HTMLCollection.js',
        'dom/DOM/elements/HTMLInputElement.js',
        'dom/DOM/elements/HTMLPreElement.js',
        'dom/DOM/elements/HTMLVideoElement.js',
        'dom/DOM/elements/HTMLUndef.js',

        'dom/DOM/documents.js',
        'dom/DOM/Image.js',
        'dom/CSS/DOMRect.js',
        'dom/WORKER/worker.js',
        'dom/WORKER/MessageChannel.js',
        'dom/speech/SpeechSynthesis.js',
        'dom/WORKER/SharedWorker.js',
        'dom/DB/indexedDB.js',
        'dom/other/ws.js',
        // 'dom/other/crypto.js',
        'dom/crypto.js',
        'dom/vm_end.js',
        // 'dom/vm_undefined.js',
        'dom/vm_undef.js',


    ];
    base_code.forEach(file => {
        code += fs.readFileSync(base_path + file, 'utf-8') + ';\n';
    })
    // console.log(code.length)
    envCodeCache = code;
    return code;
}



export function initVmSync(userCode, userConfig, html = '') {

    let $ = cheerio.load(html || fs.readFileSync('test_dom.html', 'utf-8').toString())

        // let userCodeScript = new vm.Script(userCode);
        // userCodeScript.runInContext(context)
    // console.time('env cost')

    let envCode = getEnvCode();
    //
    let scriptEnvLen = (envCode + ';;;\n;;;').split('\n').length;
    //
    let scriptENV = vm.createScript(envCode + '\n;;' + userCode, {
        // filename:userConfig.script_name,
        // lineOffset: -scriptEnvLen,
        produceCachedData: false,
        breakOnSigint: true,

    });
    // //
    // const cachedData = scriptENV.createCachedData();
    // //
    // fs.writeFileSync('cachedData.bin', cachedData);
//
//     // 从文件读取缓存数据（可选）
// const cachedData = fs.readFileSync('cachedData.bin');
//
// 使用缓存数据编译脚本
// const scriptENV = new vm.Script(envCode, { cachedData });
    let envStartTime = new Date().getTime();

    // let userCodeScript = new vm.Script(userCode)
    let prr = console.log;
    // fs.writeFileSync('cache', scriptENV.cachedData);
    // let  cachedData =   fs.readFileSync('cache')
    //  let scriptENV = vm.createScript(userCode, {
    //      cachedData: cachedData
    //  })
    let idx = 0;
    let vmConfig = {
        // CronetBuffer,
        // CronetHttpHeader,
        // CronetEngineParams,
        // CronetEngine,
        // CronetExecutor,
        // CronetUploadDataProvider,
        // CronetUrlRequestCallback,
        // CronetUrlRequestParams,
        // CronetUrlRequest,
        // cronetjs,
        // BUF,
        // ENV2: e[0]['env'],
        TextEncoder: TextEncoder,
        console: console,
        scriptEnvLen: scriptEnvLen,
        CookieJar: CookieJar,
        cc: Canvas,
        atob: atob,
        DOMException: DOMException,
        Number: Number,
        btoa: btoa,
        realFetch: fetch,
        // cloneObject: cloneObject,
        BigInt: BigInt,
        // Worker:function (url, opt) {
        //     return new Worker(url, opt)
        // },
        // postMessage:postMessage,
        // parentPort:parentPort,
        // Blob: Blob,
        // URL: URL,
        crypto: crypto,
        perfor: performance,
        newFunc: convertNewFunc,
        documentAll: createAll,
        // Crypto: crypto,
        // getFuncToString: util.getFuncToString,
        headers: {},
        // eval: newFunc('eval', 10, function (value) {
        //     function looseJsonParse(obj) {
        //         return Function('"use strict";return (' + obj + ")")();
        //     }
        //
        //     console.log('eval', value)
        //     if (value !== undefined) {
        //         value = value.replaceAll('window', 'this.window')
        //     }
        //     return new Function('"use strict";return (' + value + ")").bind(context).apply(context)
        // }),
        Exit: process.exit,
        WriteToFile: WriteToFile,
        // Function: Function,
        cyb: cyb,

        setInterval: setInterval,
        setTimeout: setTimeout,
        setImmediate: function (e){
            // console.log('setImmediate', e)
            e()
            // setTimeout(e,0)
        },
        clearInterval: clearInterval,
        clearTimeout: clearTimeout,
        timerFuncs: {
            setInterval: setInterval,
            setTimeout: setTimeout,
            setImmediate: setImmediate,
            clearInterval: clearInterval,
            clearTimeout: clearTimeout,
        },
        ccrypto: crypto,
        $: $,
        // con3: function con3() {
        //     console.log('con3', ...arguments)
        // },
        // con4: function con4() {
        //     // console.log('con4', ...arguments)
        // },
        // // con5: console.log,
        // con5: () => {
        // },
        print: function () {
            // prr(idx++)
            // prr(...arguments)
        },
        _print: function () {
            // prr(idx++)
            prr(...arguments)
        }
        // print:()=>{},
        // domUrl: 'https://www.southwest.com/air/booking/index.html?adultPassengersCount=1&adultsCount=1&departureDate=2023-07-13&d    epartureTimeOfDay=ALL_DAY&destinationAirportCode=BOS&fareType=POINTS&int=HOMEQBOMAIR&originationAirportCode=LAS&passengerType=ADULT&reset=true&returnDate=&returnTimeOfDay=ALL_DAY&tripType=oneway&validate=true',

    };
    Object.assign(vmConfig, userConfig)
    const context = vm.createContext(vmConfig);

    try {
        scriptENV.runInContext(context, {
            breakOnSigint: true,
            displayErrors: true
        });
        // userCodeScript.runInContext(context, {
        //     breakOnSigint: true,
        //     displayErrors: true
        // });
        let envEndTime = new Date().getTime();
        // console.log(`env cost ${envEndTime - envStartTime}ms`)

        let envEndTime2 = new Date().getTime();

        // console.log(`user js cost ${envEndTime2 - envEndTime}ms`)

    } catch (e) {
        prr(e)
    }
    // console.timeEnd('env cost')
    // 返回 vm 的window;
    envCode = undefined;
    return {vm, context}
// let env = res[0].env

}


// PluginArray = newFunc('PluginArray', 1, function PluginArray(len) {
//     this._length = len
// })
// aa = new PluginArray();
// aa = newFunc('PluginArray', 1, function PluginArray(len) {
//     this._length = len
// } )
// console.log(aa.toString())
// console.log(aa)
// console.log(Object.getOwnPropertyNames(aa))
// console.log(myFunction)
// console.log(myFunction())
// console.log(()=>{})
// console.log(Object.getOwnPropertyNames(myFunction))
// console.log(Object.getOwnPropertyNames(()=>{}))
// console.log(Object.getOwnPropertyNames({aa}.aa))
// console.log({aa}.aa.toString())
// console.log(aa.toString())
// console.log(aa.toString())
// console.log(aa.toString())

// getFuncToString(Function)
// console.log(Function.prototype);
// // console.log(Function.prototype.toString());
//
// Function.prototype.toString = function (){return '1'}
// console.log(Function.prototype.toString());
//
// console.log(Function.prototype);

// console.log(window.toString())
// t = cloneObject({
//     newFunc:newFunc
// })
// console.log(t.newFunc === newFunc)

